package user

import (
	"context"
	"gf-admin-api-template/internal/consts"
	"gf-admin-api-template/internal/dao"
	"gf-admin-api-template/internal/model"
	"gf-admin-api-template/internal/model/do"
	"gf-admin-api-template/internal/model/entity"
	"gf-admin-api-template/internal/service"
	"gf-admin-api-template/internal/utils/lerr"
	"github.com/gogf/gf/v2/container/gvar"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"golang.org/x/crypto/bcrypt"
)

type sUser struct{}

func init() {
	service.RegisterUser(New())
}

func New() *sUser {
	return &sUser{}
}

func (u *sUser) FindByAccount(ctx context.Context, account string) (res *model.UserInfo, err error) {
	err = dao.AdminUser.Ctx(ctx).Where(do.AdminUser{
		Account: account,
	}).Scan(&res)
	if err != nil {
		return
	}
	res.Roles, err = service.Role().GetListByUserId(ctx, []uint{res.Id})
	return
}

func (u *sUser) SetCacheUser(ctx context.Context, token string, user *model.UserInfo) (err error) {
	_, err = g.Redis().Do(ctx, "set", "user:"+token, user)
	if err != nil {
		return
	}
	_, err = g.Redis().Do(ctx, "EXPIRE", "user:"+token, 72000)
	return
}

func (u *sUser) GetCacheUser(ctx context.Context, token string) (user *model.UserInfo, err error) {
	var result *gvar.Var
	result, err = g.Redis().Do(ctx, "get", "user:"+token)
	err = result.Scan(&user)
	return
}

func (u *sUser) DelCacheUser(ctx context.Context, token string) (err error) {
	_, err = g.Redis().Do(ctx, "del", "user:"+token)
	return
}

func (u *sUser) List(ctx context.Context, in model.UserListInput) (total int, res []*model.UserInfo, err error) {
	m := dao.AdminUser.Ctx(ctx).OmitEmpty().FieldsEx(dao.AdminUser.Columns().Password).Where(do.AdminUser{
		Account:  in.Account,
		Nickname: in.Nickname,
	})
	total, err = m.Count()
	if err != nil {
		return
	}
	if total > 0 {
		err = m.Page(in.Page, in.PageSize).OrderDesc(dao.AdminUser.Columns().Id).Scan(&res)
		if err != nil {
			return
		}
		var userTemp = make(map[uint]*model.UserInfo)
		var userIds []uint
		for _, user := range res {
			userTemp[user.Id] = user
			userIds = append(userIds, user.Id)
		}
		var relList []*model.UserRelRoleInfo
		err = dao.AdminUserRelRole.Ctx(ctx).WhereIn(dao.AdminUserRelRole.Columns().UserId, userIds).Scan(&relList)
		if err != nil {
			return
		}
		var roleIds []uint
		for _, rel := range relList {
			roleIds = append(roleIds, rel.RoleId)
		}
		var roleList []*entity.AdminRole
		roleList, err = service.Role().GetListById(ctx, roleIds)
		if err != nil {
			return
		}
		var roleTemp = make(map[uint]*entity.AdminRole)
		for _, role := range roleList {
			roleTemp[role.Id] = role
		}
		for _, relRole := range relList {
			if role1, ok := roleTemp[relRole.RoleId]; ok {
				if user1, ok1 := userTemp[relRole.UserId]; ok1 {
					user1.Roles = append(user1.Roles, role1)
				}
			}
		}
	}
	return
}

func (u *sUser) Create(ctx context.Context, in model.UserCreateInput, roleIds []uint) error {
	password, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		id, err2 := dao.AdminUser.Ctx(ctx).Data(do.AdminUser{
			Nickname: in.Nickname,
			Account:  in.Account,
			Password: string(password),
		}).InsertAndGetId()
		if err2 != nil {
			return err2
		}
		var rel []*model.UserRelRoleInput
		for _, roleId := range roleIds {
			rel = append(rel, &model.UserRelRoleInput{
				UserId: uint(id),
				RoleId: roleId,
			})
		}
		_, err2 = dao.AdminUserRelRole.Ctx(ctx).Data(rel).Insert()
		return err2
	})
}

func (u *sUser) Update(ctx context.Context, id uint, in model.UserCreateInput, roleIds []uint) error {
	return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err := dao.AdminUser.Ctx(ctx).Data(in).Where(dao.AdminUser.Columns().Id, id).Update()
		if err != nil {
			return err
		}
		_, err = dao.AdminUserRelRole.Ctx(ctx).Where(dao.AdminUserRelRole.Columns().UserId, id).Delete()
		if err != nil {
			return err
		}
		var rel []*model.UserRelRoleInput
		for _, roleId := range roleIds {
			rel = append(rel, &model.UserRelRoleInput{
				UserId: id,
				RoleId: roleId,
			})
		}
		_, err = dao.AdminUserRelRole.Ctx(ctx).Data(rel).Insert()
		return err
	})
}

func (u *sUser) Delete(ctx context.Context, id uint) error {
	return g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err := dao.AdminUser.Ctx(ctx).Where(dao.AdminUser.Columns().Id, id).Delete()
		if err != nil {
			return err
		}
		_, err = dao.AdminUserRelRole.Ctx(ctx).Where(dao.AdminUserRelRole.Columns().UserId, id).Delete()
		return err
	})
}

func (u *sUser) ResetPw(ctx context.Context, id uint) error {
	password, err := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	_, err = dao.AdminUser.Ctx(ctx).Data(do.AdminUser{Password: string(password)}).Where(dao.AdminUser.Columns().Id, id).Update()
	return err
}

func (u *sUser) ChangePw(ctx context.Context, in model.UserChangePwInput) error {
	//var oldPasswordHash string
	var my = service.Auth().GetUser(ctx)
	err := bcrypt.CompareHashAndPassword([]byte(my.Password), []byte(in.OldPassword))
	if err != nil {
		return lerr.Fail(lerr.Lerr{Msg: "当前密码错误！！"})
	}
	password, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	_, err = dao.AdminUser.Ctx(ctx).Data(do.AdminUser{Password: string(password)}).Where(dao.AdminUser.Columns().Id, my.Id).Update()
	err = u.DelCacheUser(ctx, (ctx.Value(consts.AuthHeaderKey)).(string))
	return err
}
